/************************************************************************

							Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  							   cached_ptr.h

	$Header: /resource_editor/cached_ptr.h $

	$NoKeywords: $

 ************************************************************************/

#ifndef CACHED_PTR_H_INCLUDED
#define CACHED_PTR_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <assert.h>
#include "counted_ptr.h"
#include "data_lock.h"
#include "uncopyable.h"

template < typename type >
class t_cached_ptr;
template < typename type >
class t_abstract_cache;

class t_cached_ptr_base;
class t_progress_handler;
// -------------------------------------------------------------------
// t_abstract_cache_base 
// -------------------------------------------------------------------
class t_abstract_cache_base : public t_counted_object, private t_uncopyable
{
protected:
	// Member functions
	virtual int  get_load_cost();

	virtual void add_reference() = 0;
	virtual void remove_reference() = 0;

	friend t_cached_ptr_base;

	t_data_lock  m_lock;
};

typedef t_counted_ptr<t_abstract_cache_base> t_abstract_cache_base_ptr;
// -------------------------------------------------------------------
// t_abstract_cache class template
// -------------------------------------------------------------------

template < typename type >
class t_abstract_cache_data : public t_abstract_cache_base
{
public:
	// Destructor
	virtual ~t_abstract_cache_data();

private:
	// Member functions
	virtual type * do_get( t_progress_handler* handler ) = 0;

	friend class t_cached_ptr< type >;
	friend class t_abstract_cache<type>;
};

template < typename type >
t_abstract_cache_data< type >::~t_abstract_cache_data()
{
}


// -------------------------------------------------------------------
// cached pointer base
// -------------------------------------------------------------------
class t_cached_ptr_base
{
public:
	t_abstract_cache_base* get_cache() const;

protected:
	void		 add_reference( t_abstract_cache_base* cache_ptr ) const;
	t_data_lock& get_lock() const;
	void		 lock() const;
	void		 remove_reference( t_abstract_cache_base* cache_ptr ) const;
	void		 unlock() const;

	t_abstract_cache_base_ptr	m_cache;
};

inline t_abstract_cache_base* t_cached_ptr_base::get_cache() const
{
	return m_cache;
}

inline void t_cached_ptr_base::add_reference( t_abstract_cache_base * cache_ptr ) const
{
	if ( cache_ptr != 0 )
	{
		cache_ptr->m_lock.lock();
		cache_ptr->add_reference();
		cache_ptr->m_lock.unlock();
	}
}

inline t_data_lock& t_cached_ptr_base::get_lock() const
{
	return m_cache->m_lock;
}

inline void t_cached_ptr_base::lock() const
{
	if (m_cache != 0)
		m_cache->m_lock.lock();
}

inline void t_cached_ptr_base::unlock() const
{
	if (m_cache != 0)
		m_cache->m_lock.unlock();
}

inline void t_cached_ptr_base::remove_reference( t_abstract_cache_base * cache_ptr ) const
{
	if ( cache_ptr != 0 )
	{
		cache_ptr->m_lock.lock();
		cache_ptr->remove_reference();
		cache_ptr->m_lock.unlock();
	}
}

// -------------------------------------------------------------------
// cached pointer 
// -------------------------------------------------------------------

template <class type>
class t_cached_ptr : public t_cached_ptr_base
{
public:
	// Constructors/Destructor
	t_cached_ptr();
	~t_cached_ptr();

	// templated constructors - must be inlined here, due to MSC limitations.
	// construct pointer from another templated ref pointer, without typecast.

	template <class arg_type>
	explicit t_cached_ptr( const t_cached_ptr<arg_type>& pointer )
	{
		m_data = pointer.get();
		m_cache = pointer.get_cache();
		add_reference( m_cache );
	}


	// construct pointer from another templated ref pointer, without typecast.
	t_cached_ptr( const t_cached_ptr& pointer )
	{
		m_data = pointer.get();
		m_cache = pointer.get_cache();
		add_reference( m_cache );
	}

	// assign and typecast a reference counted pointer

	template <class arg_type>
	void assign( const t_cached_ptr<arg_type>& pointer )
	{
		pointer.lock();
		assign( static_cast<arg_type*>( pointer.get() ), pointer );
		pointer.unlock();
	}

/*
	// note: commented this out because the compiler wasn't flagging an assignment
	// from an unrelated pointer when this existed!!!

	// assign but don't typecast a reference counted pointer
	template <class arg_type>
	t_cached_ptr& operator=( const t_cached_ptr<arg_type>& arg )
	{
		assign( arg.get(), arg );
		return *this;
	}
*/

	// Member functions
	type*       get() const;

	// Operators
				  operator type* () const;
	type*         operator->() const;
	type&         operator*() const;
	t_cached_ptr& operator=( const t_cached_ptr& arg );
	bool          operator==( type const* arg ) const;
	bool          operator!=( type const* arg ) const;
	bool          operator==( t_cached_ptr const& arg ) const;
	bool          operator!=( t_cached_ptr const& arg ) const;
	bool          operator!() const;

private:
	// Data members
	type *					m_data;

	// Constructor
	t_cached_ptr( type * object, t_abstract_cache_base * cache );

	// Member functions
	void assign( type* data, const t_cached_ptr_base& arg );

//	friend class t_abstract_cache_data< type >;
	friend class t_abstract_cache< type >;
};

// -------------------------------------------------------------------
// inlines for cached pointer 
// -------------------------------------------------------------------
template <class type>
inline t_cached_ptr<type>::t_cached_ptr()
{
	m_cache = 0;
	m_data = 0;
}

template <class type>
inline t_cached_ptr<type>::t_cached_ptr( type* object, t_abstract_cache_base* cache )
{
	m_data = object;
	m_cache = cache;
	add_reference( cache );
}

template <class type>
inline t_cached_ptr<type>::~t_cached_ptr()
{
	remove_reference( m_cache );
}


template <class type>
inline type* t_cached_ptr<type>::get() const
{
	return m_data;
}

template <class type>
inline t_cached_ptr<type>::operator type* () const
{
	return m_data;
}

template <class type>
inline void t_cached_ptr<type>::assign( type* data, t_cached_ptr_base const& arg )
{
	t_abstract_cache_base* other_cache = arg.get_cache();

	add_reference( other_cache );
	remove_reference( m_cache );
	m_cache = other_cache;
	m_data = data;
}

template <class type>
inline t_cached_ptr<type>& t_cached_ptr<type>::operator=( t_cached_ptr const& arg )
{
	assign( arg.get(), arg );
	return *this;
}

template <class type>
inline type* t_cached_ptr<type>::operator->() const
{
	return m_data;
}

template <class type>
inline type& t_cached_ptr<type>::operator*() const
{
	return *m_data;
}

template <class type>
inline bool t_cached_ptr<type>::operator==( type const* arg ) const
{
	return m_data == arg;
}

template <class type>
inline bool t_cached_ptr<type>::operator!=( type const* arg ) const
{
	return m_data != arg;
}

template <class type>
inline bool t_cached_ptr<type>::operator==( t_cached_ptr const& arg ) const
{
	return m_data == arg.m_data;
}

template <class type>
inline bool t_cached_ptr<type>::operator!=( t_cached_ptr const& arg ) const
{
	return m_data != arg.m_data;
}

template <class type>
inline bool t_cached_ptr<type>::operator!() const
{
	return m_data == 0;
}

// -------------------------------------------------------------------
// t_abstract_cache class template
// -------------------------------------------------------------------
template < typename type >
class t_abstract_cache
{
public:
	t_abstract_cache();
	virtual ~t_abstract_cache();

	bool               is_valid() const;
	t_cached_ptr<type> get( t_progress_handler* = 0 ) const;
	int                get_load_cost() const;
	bool               operator==( t_abstract_cache const& arg ) const;
	bool               operator!=( t_abstract_cache const& arg ) const;
protected:
	typedef t_abstract_cache_data<type> t_data;

	t_abstract_cache( t_data* cache );

	void set( t_data* cache );
private:
	t_counted_ptr<t_data> m_cache;
};

template <typename type>
t_abstract_cache<type>::t_abstract_cache()
{
}

template < typename type >
t_abstract_cache<type>::t_abstract_cache( t_data* cache ) : m_cache( cache )
{
}

template < typename type >
t_abstract_cache<type>::~t_abstract_cache()
{
}

template < typename type >
inline bool t_abstract_cache<type>::is_valid() const
{
	return m_cache != 0;
}

template < typename type >
inline int t_abstract_cache<type>::get_load_cost() const
{
	return m_cache->get_load_cost();
}

template < typename type >
inline bool t_abstract_cache<type>::operator==( t_abstract_cache const& arg ) const
{
	return m_cache == arg.m_cache;
}

template < typename type >
inline bool t_abstract_cache<type>::operator!=( t_abstract_cache const& arg ) const
{
	return m_cache != arg.m_cache;
}


template < typename type >
inline t_cached_ptr<type> t_abstract_cache<type>::get( t_progress_handler* handler ) const
{
	assert( m_cache != 0 );

	type*         data_ptr;

	m_cache->m_lock.lock();
	m_cache->add_reference();
	data_ptr = m_cache->do_get( handler );

	t_abstract_cache_base* cache_ptr = data_ptr != 0 ? m_cache : 0;
	t_cached_ptr<type>     result( data_ptr, cache_ptr );

	m_cache->remove_reference();
	m_cache->m_lock.unlock();
	return result;
}

template < typename type >
inline void t_abstract_cache<type>::set( t_data* cache )
{
	m_cache = cache;
}

#endif // CACHED_PTR_H_INCLUDED
